Chapter 4: Functions Part 1
From book Python Programming (Problem solving, Packages and Libraries) published by McGraw Hill Education (India) Private limited.
By:
Note the following:-
>>> symbol and therefore cannot be directly executed. If you want to execute them on IDLE or Jupyter, you need to manually remove the >>> symbol.Markdown and code cells (extension .ipynb) and then downloaded as html. If someone wants to "modify" or "extend' this document, you may ask for the original .ipynb file by sending me an e-mail at:- 999.anuraggupta@gmail.com4.3.3 Calling a function (As opposed to defining or writing a function)
# ---ON IDLE---
>>>import os
>>> myCwd = os.getcwd() # Function which takes no arguments
>>> myCwd
'C:\\Python34'
>>> int(9.1234) # Function casts float into int. Takes 1 argument
9
>>> curPath = os.chdir('C:\\') #Function which returns None always
>>>print(curPath)
None
>>> os.getcwd()
'C:\\'
4.3.4 Some important built-in functions in Python
Python has a number of “built-in” functions. Some important ones are as follows:
(i) abs(x)
This function:
The following examples clarify the concepts:
# ---ON IDLE---
>>> abs(-9)
9
>>> abs(3 + 4j) # abs is under root of 3 square plus 4 square
5.0
>>> abs(-3 -4j) # abs is under root of -3 square and -4 square
5.0
(ii) bool([x])
The use of square brackets indicates that the parameter is optional. If you don’t give a parameter to the bool() function, it will return a False.
This function does the following:
bool() works:-
# ---ON IDLE---
>>> bool() # bool() ie without an argument is False
False
>>> bool(None) #bool() with None is False
False
>>> bool(False) # bool() of False is False
False
>>> bool(0) #bool() of 0 is False
False
>>> bool([]) # bool() of an empty list is False
False
>>> bool(()) #bool() of an empty tuple is False
False
>>> bool(-1) # bool() of a non-zero number even if negative is True
True
(iv) cmp(x, y)
The cmp(x,y) function does the following:
Example code:-
# ---ON IDLE---
>>>True + True # If a plus between two True, they are cast to int
2
>>>True + False # False is cast into int 0
1
>>>False + False
0
So if x is greater than y then $((x >y) – (x < y))$ will become 1, if x is equal to y, it will be 0 and if x is less than y, it will be -1.
# ---ON IDLE---
>>> (5>3) - (5<3) #Implicit cast of bool to int
1
>>> (int(5>3) - int(5<3)) #Explicit cast of bool to int
1
>>> (3>5) - (3<5)
-1
>>> (5 ==5) - (5 == 5)
0
(v) divmod(x,y)
The divmod(x, y) function does the following:
# ---ON IDLE---
>>> divmod(29,5) # 29 is dividend and 5 is divisor
(5, 4)
>>> divmod(1.5, 0.9) # works for floats also
(1.0, 0.6)
(vi) float(x)
The important points regarding this function are as follows:
# ---ON IDLE---
>>> float(2) # Convert an int to float
2.0
>>> float('-345.6') #Since string has only digits, sign ie '-' and decimal so OK
-345.6
>>> float(2e3) #Can convert number in exponential form
2000.0
>>> float(-3E-3)# Exponential form can be with 'e' or 'E' and with '-' sign also
-0.003
>>> float('abc') #String 'abc' cannot be converted to float -> error
Traceback (most recent call last):
...rest of error message.....
ValueError: could not convert string to float: 'abc'
(vii) id(object)
The id(object) function does the following:
id() function as a unique number given to each object. Example code:
# ---ON IDLE---
>>> myS = 'abc'
>>> id(myS) # String objects like all objects have id
4690496
>>> id(2) # Even integers (like 2) have id
1474150432
(viii) int(x)
The important features of this function are as follows:
int(x) converts a number or string x to an integer. If no argument is given to the int() function, it returns 0.This is clear from the following example:
# ---ON IDLE---
>>> x = int() #If no argument to int() returns a 0
>>> x
0
>>> myNum = 3.2# Lets take a float
>>> myInt = int(myNum) #Use int to create ie return the int of myNum
>>> myInt #myInt is an integer
3
>>> myNum # But myNum continues to be float
3.2
>>> mySt = '123'# Take a string
>>> myInt2 = int(mySt) # Again int() returns string equivalent of mySt
>>> mySt # mySt continues to be a string
'123'
>>> myInt2 # But myInt2 is an integer
123
>>> int('45L') #Will throw error as string '45L' not convertible to int
... rest of error ...
ValueError: invalid literal for int() with base 10: '45L'
>>> int('12.3') # However you cannot convert to int a string with decimal
... rest of error ...
ValueError: invalid literal for int() with base 10: '12.3'
(ix) len(x)
The len(x) function does the following:
Example code:
# ---ON IDLE---
>>> len(['a', 'b', 'c']) # 3 items in list
3
>>> len('123456789') # 9 characters in the string
9
(x) max(s), or max(arg1, arg2, arg3, .... argN) function
There are two variations of the max function:
1. max(s)` where s is a non-empty iterable object, such as a string, list, tuple, and so on.
This will be clear from the following:-
# ---ON IDLE---
>>> max('abcdefg') # you can give a string to max() because string is iterable
'g'
>>> max([1,2,3,4]) #List is also iterable
4
>>> max([]) #Empty list will give error
Traceback (most recent call last):
File "<pyshell#66>", line 1, in<module>
max([])
ValueError: max() arg is an empty sequence
2. max(arg1, arg2, arg3,...argN)
Here arg1, arg2, arg3,... argN are the arguments given and then the max() function returns the largest of these given arguments. Note that if the arguments are strings, then the max function will return the string beginning with the character with the largest Unicode.
his is shown as follows:-
# ---ON IDLE---
>>> max(22,33,99,44,55,66,00)
99
>>> max('a', 'A') #Unicode of 'a' is more than 'A'
'a'
(xi) min(s), or min(arg1, arg2, arg3, .... argN) function
Just like the max() function, there are two variations of the min() function:
# ---ON IDLE---
>>> min('aAbBcC')
‘A’
2. min(arg1, arg2, arg3,...argN)
Here arg1, arg2, arg3,... argN are the arguments given and then the max() function returns the smallest of these given arguments.
# ---ON IDLE---
>>> min([2,8,1,0,-3,100])
-3
(xii) range(start, stop[, step])
Some important points to note about the range(start, stop[, step]) are as follows:
range(n) or range(stop) is discussed. Moreover, it is presumed that n is a positive integer. (Other forms of this function with negative integer etc also exist but are discussed later). range(n) will generate a sequence of numbers from 0 to n-1. range(5), output is a list, that is, [0, 1, 2, 3, 4]. But in Python 3.x, the output is not a list. Rather, it is a “range object” which is iterable, that is, which can be moved over one by one and can also be converted into other Python objects, such as a list. This is shown as follows:-
# ---ON IDLE---
>>> range(5) # In Python 2.x
[0,1,2,3,4]
>>> range(5) # In Python 3.x
range(0, 5)
>>> list(range(5)) #In 3.x if you want a list, you need to use list() function
[0, 1, 2, 3, 4]
(xiii) round(number[, ndigits])
The function round(number [, ndigits]) does the following:-
This is clear from the following example:
# ---ON IDLE---
>>> round(2.345)# Second parameter not given so rounded to 0 digits after decimal
2
>>>round(2.345,1) # Second parameter is 1. So rounded to 1 digit after decimal
2.3
(xiv) str(object='')
The function str(object= '') does the following:
str(object), returns the empty string. You can think of the str() function in two different ways:
str() function then an “empty” string is created. This is shown as follows:
# ---ON IDLE---
>>> str(1.2345) # a float object is cast into a string
'1.2345'
>>> str(1==1) #Outcome of 1 ==1 is True which is cast to string
'True'
>>> str() # No parameter to str() so an empty string created
''
str() function is that it creates a “string representation” of an object. This topic is covered later in the book.(xv) tuple([iterable])
The function tuple([iterable]) does the following:
tuple([iterable]), converts the iterable into a tuple and returns it. Remember that a tuple is a sequence so the “order of items” is important. So when an iterable is converted into a tuple by the tuple([iterable]) function, the order of items in the tuple is the same as in the iterable.Example code:-
# ---ON IDLE---
>>> tuple('abcd') #Since string is a sequence so convertible to tuple using tuple()
('a', 'b', 'c', 'd')
>>> tuple([1,2,3]) #List also convertible to tuple using tuple()
(1, 2, 3)
>>> tuple(('a', 'b', 'c')) # A tuple can be given as argument to tuple()
('a', 'b', 'c')
>>> tuple() #If no parameter, empty tuple created
()
(xvi) any(iterable) function
The any() function takes an iterable as its argument. The concept of an iterable is explained later. For the present, you can think of an iterable as a container, such as a string, list, tuple, and so on. If any item in the iterable evaluates to True, then the function returns True.
myL = [1, 2, 3, 4]
print(any(myL)) # gives True
myS = '' # Empty string
print(any(myS)) # gives False
myT = () # Empty tuple
print(any(myT)) # gives False
4.3.5 Some important functions in modules in Python
The following script shows use of some important functions of math module:- (Shown here is use of functions ceil(), fabs() and floor())
# ---ON IDLE---
>>>import math
>>> math.ceil(7.001)
8
>>> math.fabs(-12)
12.0
>>> math.floor(-7.99) # floor() of -7.99 is -8 and not -7 (-7 would be truncate)
-8
>>> math.ceil(-7.99) #ceil() of -7.99 will be -7 not -8
-7
math.exp(x) function
The function exp(x) returns $e^x$.
Following script shows this:-
# ---ON IDLE---
>>>math.exp(1) # This will give value of e. Since e**1 -> e
2.718281828459045
>>> math.exp(2) # This will give value of e ** 2
7.38905609893065
math.log(x[, base])
The function math.log(x[, base]) does the following:
e, which is available as math.e and its value is 2.718281828459045. # ---ON IDLE---
>>> math.log(math.e) # math.e will give value of e whose natural log is 1
1.0
>>> math.log(7.39)# Also 7.39 little larger than e ** 2 so log(7.39) approx 2
2.0001277349601105
>>> math.log(100) # e ** 4.60 is approx 100
4.605170185988092
>>> math.log(100,10) # 10 ** 2 -> 100
2.0
math.pow(x, y)
The method pow(x, y) does the following:
** operator, math.pow() converts both its arguments to type float.pow(x,y) is also a built-in function in Python so you can use pow(x,y) directly, that is, without using math.pow(x,y) also. It is also available as x**y. You should use ** or the built-in pow() function for computing exact integer powers (that is, when both x and y are integers).# ---ON IDLE---
>>>pow(2,2) #Inbuilt pow(x,y) function. For integers returns int
4
>>>math.pow(2,2) # pow(x,y) function of math module. Returns float
4.0
>>>2**2# Inbuilt exponential operator in python. Returns int for integers
4
math.degrees(x)
This method converts the given angle x from radians to degrees.
math.radians(x)
This method converts the given angle x from degrees to radians.
Note that the math module has a mathematical constant pi. This can be accessed as math.pi and is 3.141592653589793.
# ---ON IDLE---
>>> math.sin(math.pi/2) # sin(π/2) is 1.0 and it is float
1.0
>>> math.cos(math.pi/3) # cos(π/3) is 0.5 and float
0.5000000000000001
>>>math.tan(math.pi/4) # tan(π/4) is 1.0 which here is 0.9999...
0.9999999999999999
random.random()
The following points regarding random.random() are important:
# ---ON IDLE---
>>>import random
>>> random.random() #Generates random number in range [0,1)
0.1453192289647084
>>> int(random.random() * 100) #If you want random int in [0, 100)ie 0 to 99
73
random.seed([a = None])
The following points regarding the method random.seed([a = None]) are noteworthy:
seed() method, then this object must be a “hashable” object. random([a]), where “a” is an immutable object. Since integers, floats, strings, and so on are immutable, they can be used as arguments to random.seed() function.# ---ON IDLE---
>>> random.seed(25) # can seed with an int
>>> random.random()
0.376962302390386
>>> random.random()
0.9267885077263207
>>> random.seed(25) # If you seed with same int, the series is repeated
>>> random.random()
0.376962302390386
>>> random.random()
0.9267885077263207
random.choice(sequence)
Here, sequence can be any sequence. So sequence could be a string, list or tuple. This method returns an item randomly selected from the given sequence.
# ---ON IDLE---
>>> random.choice('abcdefghijklmnopqrstuvwxyz') # A string is a sequence
'p'
>>> random.choice(range(1000)) # Return of range() function is also a sequence
43
>>> random.choice([1,2,3,4,5,6,7,8,9]) # List is a sequence
5
random.uniform(ax, y)
This method gives a random floating point number f such that $x ≤ f ≤ y$ for $x ≤ y$. If $x ≥ y$, then you get $x ≥ f ≥ y$.
Some examples are as follows:
# ---ON IDLE---
>>> random.uniform(1, 100) # For x <= N <= y
68.98105318225107
>>> random.uniform(100,1) # For y <= N <= x
17.608188185718944
>>> random.uniform(1,1) # For x = y, always x (or y)
1.0
randrange([start], stop, [step])
The following regarding random.randrange([start], stop, [step]) function are noteworthy:
Return a randomly-selected element from range(start, stop, step).
Difference between randrange([start[, stop, [step]) and range(start, stop, step) is that randrange([start[, stop, [step]) creates a range of integers. For instance, range(3, 10, 2) -> will include 3, 5, 7, 9. But randrange(3, 10, 2) will give ONE of the integers 3, 5, 7, 9.
# ---ON IDLE---
>>> random.randrange(3,10,2) # Possible outcomes are one of 3,5,7,9
7
4.4.1 Syntax for writing / defining your function
To define a function, you have to follow a definite syntax ,as follows:
# ---ON JUPYTER---
def func_name(arg1, arg2, ..., argN):
statements (Optional)
return some_object (Optional)
2. Flow of execution of a function
The first thing you need to understand is that every function has a function definition and a function call. Note the following:
The following code explains the concept:
# ---ON IDLE---
>>>def f1(a):
x = a
print('Argument passed-> ', x)
>>> f1(5) # Function call with integer 5
Argument passed->5
>>> f1('cat') # Function call with string ‘cat’
Argument passed-> cat
>>> f1([1,2,3,4]) # Function call with list [1, 2, 3, 4]
Argument passed-> [1, 2, 3, 4]
The following example clarifies the “flow of execution” where there is a function definition and a function call:-
def f1():
print('Inside f1()')
print('Lets call f1()') #First line of executable code
f1() # Call to function f1()
print('Terminate')
4.3.7 Scope namespace and lifetime of variables
(iii) Variable inside a function definition (Example)
def f1(a):
y = 'dog'# a and y are local variables
print(y, a) # OK since y is defined here
x = 'cat'# x is a global variable. But y doesnt exist here
f1(x)
print(y) #Not OK. ERROR since y has gone "out of scope"
(iv) Name clash in local and global scope
What does name clash mean? In Python you can assign different values to a variable name a number of times and the variable will point to the value by the latest assignment:
# ---ON IDLE---
x = 'cat'
print('x is-> ', x) # x is cat
x = 'dog'
print('Now x is -> ', x) # x becomes dog
But if you assign different values to the SAME variable name in DIFFERENT scopes, then you have what is a potential name clash. How does Python interpreter resolve this? It follows a simple rule:
This is best understood by an example:-
x = 'cat'
def f3():
x = 'dog'
print('x inside the function call is-> ', x)
f3()
print('x in global scope is-> ', x)
(v) Creating two local scopes in a global scope.
One can create a number of different local scopes within a global scope. This is best understood by the following example:
x = 'cat'
def f1():
x = 'dog'
print('x in f1-> ', x)
def f2():
x = 'rat'
print('x in f2-> ', x)
f1() # Prints x in scope of f1()
f2() # Prints x in scope of f2()
print("x in global scope-> ",x)
(vi) Defining a function inside another function(Nested functions)
In Python it is permitted to define one function inside another function (Also called nested functions). But if you do so you cannot access the inner function from out of the enclosing function. In general, this is not a good programming practice. The following example shows the concept. Here, f1() is the enclosing, that is, outer function and f2() is the enclosed, that is, inner function.
x = 'cat'
def f1():
x = 'dog'
print('x in f1-> ', x)
def f2():
x = 'rat'
print('x in f2-> ', x)
f2()# You can call f2() here, since this is scope of f1()
f1()
# f2(x) is not callable from global scope
print('x in global scope-> ', x)
(vii) Order of search in nested functions
Note that nested functions create nested scope. In the above example, there was an enclosing function f1() and there was a nested function f2(). Both f1() and f2() defined x inside its scope. But suppose the nested function f2() did not define a value of x and it was asked to print x, what would it print? It would print the value of x in f1(). This is shown as follows:-
x = 'cat'
def f1(a):
x = 'dog'
print('x in f1-> ', x)
def f2(a):
#x = 'rat'
print('x in f2-> ', x)
f2(x) # This is scope of f1
f1(x) # Will print x of scope of f1
# f2(x) is not callable from global scope
print('x in global scope -> ', x)